package cn.school.attendance.classes.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.school.attendance.classes.dao.ClassesAttendanceInfoMapper;
import cn.school.attendance.classes.entity.ClassesAttendanceDo;
import cn.school.attendance.classes.entity.Page;
import cn.school.attendance.classes.service.ClassesAttendanceInfoService;
import cn.school.attendance.classes.vo.ClassesAttendanceInfoPageParam;
import cn.school.attendance.classes.vo.ClassesAttendanceVo;
import cn.school.attendance.classes.vo.LeaveAndNotArrivedStudentInfoVo;
import cn.school.common.utils.DateTimeUtils;
import cn.school.student.dao.StudentInfoDao;
import cn.school.student.dao.StudentTrajectoryMapper;
import cn.school.student.entity.StudentInfo;
import cn.school.student.entity.StudentTrajectory;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.collections4.ListUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service
public class ClassesAttendanceInfoServiceImpl extends ServiceImpl<ClassesAttendanceInfoMapper, ClassesAttendanceDo> implements ClassesAttendanceInfoService {
    @Resource
    private ClassesAttendanceInfoMapper classesAttendanceInfoMapper;
    @Resource
    private StudentTrajectoryMapper studentTrajectoryMapper;
    @Resource
    private StudentInfoDao studentInfoDao;

    //更新学生在校状态
    @Override
    public void updateAttendanceInfoStatus(ClassesAttendanceDo classesAttendanceInfo) {
        if ("1".equals(classesAttendanceInfo.getStatus())) {
            classesAttendanceInfo.setArrivalDate(new Date());
        } else if ("2".equals(classesAttendanceInfo.getStatus())) {
            classesAttendanceInfo.setDepartureDate(new Date());
        }
        classesAttendanceInfo.setTeacherUpdate("1");
        classesAttendanceInfoMapper.updateAttendanceInfoStatus(classesAttendanceInfo);
    }

    //获取学生考勤列表
    @Override
    public ClassesAttendanceVo getStudentAttendancePageList(ClassesAttendanceInfoPageParam classesAttendanceInfoPageParam) {
        ClassesAttendanceVo studentAttendanceInfo = classesAttendanceInfoMapper.getClassAttendanceInfo(classesAttendanceInfoPageParam);
        int count = classesAttendanceInfoMapper.getStudentAttendancePageListCount(classesAttendanceInfoPageParam);
        int startNum = (classesAttendanceInfoPageParam.getPageNum() - 1) * classesAttendanceInfoPageParam.getPageSize();
        Page page = new Page();
        page.setPageSize(classesAttendanceInfoPageParam.getPageSize());
        page.setPageNum(classesAttendanceInfoPageParam.getPageNum());
        page.setPageCount(count);
        classesAttendanceInfoPageParam.setPageNum(startNum);
        List<ClassesAttendanceDo> studentAttendanceList = classesAttendanceInfoMapper.getStudentAttendanceList(classesAttendanceInfoPageParam);
        page.setContent(studentAttendanceList);
        studentAttendanceInfo.setStuList(page);
        return studentAttendanceInfo;
    }

    //查询门禁记录是否有更新
    @Override
    public void checkStudentTrajectoryUpdate(ClassesAttendanceInfoPageParam param) {
        // 需要插入的学生列表
        List<ClassesAttendanceDo> insertStudentAttendanceList = new ArrayList<>();

        // 需要更新的学生列表
        List<ClassesAttendanceDo> updateStudentAttendanceList = new ArrayList<>();
        //判断attendanceDate如果是当前日期则传入new Date 如果不是传入日期 + 23：59：59
        String attendanceDate = param.getAttendanceDate();
        if (DateTimeUtils.isToday(attendanceDate)) {
            attendanceDate = DateTimeUtils.getCurrentDate();
        } else {
            attendanceDate = attendanceDate + " 23:59:59";
        }
        param.setAttendanceDate(attendanceDate);
        // 当日请假的学生Id

        List<Long> selfLeaveStudentIdList = classesAttendanceInfoMapper.getSelfLeaveStudentIdList(param);
        List<Long> leaveStudentIdList = classesAttendanceInfoMapper.getLeaveStudentIdList(param);
        // 所有请假学生 去重
        List<Long> allLeaveStudentIdList = Stream.concat(
                leaveStudentIdList.stream(),
                selfLeaveStudentIdList.stream()
        ).distinct().collect(Collectors.toList());

        // 查询班级所有学生id
        List<Long> studentIds = studentInfoDao.selectList(new LambdaQueryWrapper<StudentInfo>().eq(StudentInfo::getClassId, param.getClassId())).stream().map(StudentInfo::getId).collect(Collectors.toList());
        //过滤所有学生列表中的请假学生id
        List<Long> noLeaveStudentIdList = ListUtils.subtract(studentIds, allLeaveStudentIdList);

        // 查询住校学生列表
        List<Long> dormitoryStudentIds = classesAttendanceInfoMapper.getStudentIdsByClassId(param.getClassId());
        //过滤住请假的住校生
        List<Long> noLeaveDormitoryStudentIdList = ListUtils.subtract(dormitoryStudentIds, allLeaveStudentIdList);
        // 走读生Id列表
        List<Long> notLeaveDayStudentIds = ListUtils.subtract(noLeaveStudentIdList, noLeaveDormitoryStudentIdList);

        // 查询当前班级所有学生当天刷脸时间最晚的门禁记录以及类型（进/出校）
        List<StudentTrajectory> studentTrajectoryList = studentTrajectoryMapper.getStudentTrajectoryByClassIdAndAttendanceDate(param.getClassId(), param.getAttendanceDate());

        // 查询当前班级所有学生考勤记录
        List<ClassesAttendanceDo> studentAttendanceList = classesAttendanceInfoMapper.getAllStudentAttendanceListByClassId(param.getClassId(), param.getAttendanceDate());

        // 排除已请假学生得到该班级未请假学生门禁记录
        List<StudentTrajectory> notLeaveStudent = studentTrajectoryList.stream()
                .filter(student -> !allLeaveStudentIdList.contains(student.getStudentId()))
                .collect(Collectors.toList());

        // 获取当天存在门禁记录且未请假的走读生(走读已到)
        List<StudentTrajectory> dayStudentTrajectoryList = notLeaveStudent.stream()
                .filter(studentTrajectory -> notLeaveDayStudentIds.contains(studentTrajectory.getStudentId()))
                .collect(Collectors.toList());

        // 获取存在门禁记录且type不为2的住校生Id(住校已到)
        List<StudentTrajectory> dormitoryStudentTrajectoryList = notLeaveStudent.stream()
                .filter(studentTrajectory -> dormitoryStudentIds.contains(studentTrajectory.getStudentId()))
                .filter(studentTrajectory -> !studentTrajectory.getType().equals("2"))
                .collect(Collectors.toList());

        // 获取未请假且无门禁记录的住校生Id(住校已到)
        List<Long> notArrivedDormitoryStudentIdList = noLeaveDormitoryStudentIdList.stream()
                .filter(id -> notLeaveStudent.stream().noneMatch(student -> id.equals(student.getStudentId())))
                .collect(Collectors.toList());

        // 获取未请假且无门禁记录的走读学生Id(走读未到)
        List<Long> notArrivedDayStudentIdList = notLeaveDayStudentIds.stream()
                .filter(id -> !notLeaveStudent.stream()
                        .map(StudentTrajectory::getStudentId)
                        .collect(Collectors.toList())
                        .contains(id)
                ).collect(Collectors.toList());

        // 获取未请假住校生当天门禁记录为离校状态的记录(住校未到)
        List<StudentTrajectory> notArrivedDormitoryStudentNotTrajectoryList = notLeaveStudent.stream()
                .filter(studentTrajectory -> noLeaveDormitoryStudentIdList.contains(studentTrajectory.getStudentId()))
                .filter(studentTrajectory -> studentTrajectory.getType().equals("2"))
                .collect(Collectors.toList());
        //获取请假的走读生
        List<Long> leaveDayStudentIds = allLeaveStudentIdList.stream()
                .filter(id -> !dormitoryStudentIds.contains(id))
                .collect(Collectors.toList());
        //获取请假的住校生
        List<Long> leaveDormitoryStudentIds = allLeaveStudentIdList.stream()
                .filter(dormitoryStudentIds::contains)
                .collect(Collectors.toList());

        //在校走读插入/更新考勤
        processStudentTrajectories(dayStudentTrajectoryList, studentAttendanceList, param.getAttendanceDate(), insertStudentAttendanceList, updateStudentAttendanceList,"1");
        //请假走读生插入/更新考勤
        processStudentTrajectories(leaveDayStudentIds, studentAttendanceList, param.getAttendanceDate(), insertStudentAttendanceList, updateStudentAttendanceList, "1","3");
        //走读生(无门禁)插入/更新考勤
        processStudentTrajectories(notArrivedDayStudentIdList, studentAttendanceList, param.getAttendanceDate(), insertStudentAttendanceList, updateStudentAttendanceList,"1","4");
        //住校生(无门禁)插入/更新考勤
        processStudentTrajectories(notArrivedDormitoryStudentIdList, studentAttendanceList, param.getAttendanceDate(), insertStudentAttendanceList, updateStudentAttendanceList, "2","4");
        //住校生(有门禁/在校)插入/更新考勤
        processStudentTrajectories(dormitoryStudentTrajectoryList, studentAttendanceList, param.getAttendanceDate(), insertStudentAttendanceList, updateStudentAttendanceList,"2");
        //住校生(有门禁/离校)插入/更新考勤
        processStudentTrajectories(notArrivedDormitoryStudentNotTrajectoryList, studentAttendanceList, param.getAttendanceDate(), insertStudentAttendanceList, updateStudentAttendanceList,"2");
        //请假住校生插入/更新考勤
        processStudentTrajectories(leaveDormitoryStudentIds, studentAttendanceList, param.getAttendanceDate(), insertStudentAttendanceList, updateStudentAttendanceList, "2","3");
        //住校生(离校)插入/更新考勤
//        List<Long> notArrivedDormitoryStudentIds = notArrivedDormitoryStudentNotTrajectoryList.stream().map(StudentTrajectory::getStudentId).collect(Collectors.toList());
//        processStudentTrajectories(notArrivedDormitoryStudentIds, studentAttendanceList, param.getAttendanceDate(), insertStudentAttendanceList, updateStudentAttendanceList, "2","2");


        //批量插入学生考勤记录
        if (insertStudentAttendanceList.size() > 0) {
            List<List<ClassesAttendanceDo>> partition1 = ListUtils.partition(insertStudentAttendanceList, 100);
            for (List<ClassesAttendanceDo> item : partition1) {
                classesAttendanceInfoMapper.insertBatch(item);
            }
        }

        //批量更新学生考勤记录
        if (updateStudentAttendanceList.size() > 0) {
            List<List<ClassesAttendanceDo>> partition1 = ListUtils.partition(updateStudentAttendanceList, 100);
            for (List<ClassesAttendanceDo> item : partition1) {
                classesAttendanceInfoMapper.updateBatch(item);
            }
        }
    }


    //获取请假学生列表
    @Override
    public List<LeaveAndNotArrivedStudentInfoVo> getLeaveStudentInfoList(ClassesAttendanceInfoPageParam classesAttendanceInfoPageParam) {
        String date = classesAttendanceInfoPageParam.getAttendanceDate();
        String s = DateUtil.formatTime(new Date());
        classesAttendanceInfoPageParam.setAttendanceDate(date  + " "+ s);
        List<LeaveAndNotArrivedStudentInfoVo> allLeaveStudent = classesAttendanceInfoMapper.getLeaveStudentInfoList(classesAttendanceInfoPageParam);
        return allLeaveStudent;
    }

    @Override
    public List<LeaveAndNotArrivedStudentInfoVo> getNoArrivedStudent(ClassesAttendanceInfoPageParam classesAttendanceInfoPageParam) {
        //获取未到校学生列表
        List<LeaveAndNotArrivedStudentInfoVo> hasArrivedStudentList = classesAttendanceInfoMapper.getNotArrivedStudentInfoList(classesAttendanceInfoPageParam);
        return hasArrivedStudentList;
    }

    //根据状态修改学生考勤时间
    public ClassesAttendanceDo changeStudentAttendanceTime(ClassesAttendanceDo classesAttendanceDo, StudentTrajectory studentTrajectory, Date faceTime,String attendanceDate) {
        classesAttendanceDo.setStatus(studentTrajectory.getType());
        classesAttendanceDo.setStudentId(studentTrajectory.getStudentId());
        if ("1".equals(studentTrajectory.getType())) {
            classesAttendanceDo.setArrivalDate(faceTime);
        } else if ("2".equals(studentTrajectory.getType())){
            classesAttendanceDo.setDepartureDate(faceTime);
        }
        return classesAttendanceDo;
    }

    private void processStudentTrajectories(List<StudentTrajectory> studentTrajectoryList,
                                            List<ClassesAttendanceDo> studentAttendanceList,
                                            String attendanceDate,
                                            List<ClassesAttendanceDo> insertStudentAttendanceList,
                                            List<ClassesAttendanceDo> updateStudentAttendanceList,
                                            String studentType) {
        //存在考勤数据的学生
        List<ClassesAttendanceDo> studentAttendanceListByStudentId = studentAttendanceList.stream()
                .filter(studentAttendanceDo -> studentTrajectoryList.stream()
                        .anyMatch(studentTrajectory ->
                                studentTrajectory.getStudentId().equals(studentAttendanceDo.getStudentId())))
                .collect(Collectors.toList());
        //不存在考勤数据的学生
        List<StudentTrajectory> studentTrajectoryListByStudentId = studentTrajectoryList.stream()
                .filter(studentTrajectory ->
                        studentAttendanceList.stream()
                        .noneMatch(studentAttendanceDo ->
                                studentAttendanceDo.getStudentId().equals(studentTrajectory.getStudentId())))
                .collect(Collectors.toList());
        for (StudentTrajectory studentTrajectory : studentTrajectoryList) {
            for (ClassesAttendanceDo studentAttendanceDo : studentAttendanceListByStudentId) {
                if (studentTrajectory.getStudentId().equals(studentAttendanceDo.getStudentId())
                && !studentAttendanceDo.getStatus().equals(studentTrajectory.getType())
                && studentAttendanceDo.getTeacherUpdate() == null){
                    Date faceTime = DateTimeUtils.paresDate(studentTrajectory.getFaceTime());
                    studentAttendanceDo = changeStudentAttendanceTime(studentAttendanceDo, studentTrajectory, faceTime,attendanceDate);
                    studentAttendanceDo.setStudentPicture(studentTrajectory.getSnapFtpUrl());
                    studentAttendanceDo.setAttendanceDate(attendanceDate);
                    studentAttendanceDo.setModifyTime(new Date());
                    updateStudentAttendanceList.add(studentAttendanceDo);
                }
            }
        }
        for (StudentTrajectory studentTrajectory : studentTrajectoryListByStudentId) {
            Date faceTime = DateTimeUtils.paresDate(studentTrajectory.getFaceTime());
            ClassesAttendanceDo newStudentAttendanceDo = changeStudentAttendanceTime(new ClassesAttendanceDo(), studentTrajectory, faceTime,attendanceDate);
            newStudentAttendanceDo.setStudentPicture(studentTrajectory.getSnapFtpUrl());
            newStudentAttendanceDo.setStudentType(studentType);
            newStudentAttendanceDo.setAttendanceDate(attendanceDate);
            newStudentAttendanceDo.setCreateTime(new Date());
            insertStudentAttendanceList.add(newStudentAttendanceDo);
        }
    }

    private void processStudentTrajectories(List<Long> stuList, List<ClassesAttendanceDo> studentAttendanceList, String attendanceDate, List<ClassesAttendanceDo> insertStudentAttendanceList, List<ClassesAttendanceDo> updateStudentAttendanceList, String studentType,String type) {
        for (Long stuId : stuList) {
            if (studentAttendanceList.stream().noneMatch(studentAttendanceDo -> studentAttendanceDo.getStudentId().equals(stuId))) {
                ClassesAttendanceDo studentAttendanceDo = new ClassesAttendanceDo();
                studentAttendanceDo.setStudentType(studentType);
                studentAttendanceDo.setStudentId(stuId);
                studentAttendanceDo.setAttendanceDate(attendanceDate);
                studentAttendanceDo.setCreateTime(new Date());
                studentAttendanceDo.setStatus(type);
                insertStudentAttendanceList.add(studentAttendanceDo);
            }else{
                ClassesAttendanceDo studentAttendanceDo = new ClassesAttendanceDo();
//                studentAttendanceDo.setStudentType(studentType);
                studentAttendanceDo.setStudentId(stuId);
                studentAttendanceDo.setAttendanceDate(attendanceDate);
                studentAttendanceDo.setModifyTime(new Date());
                studentAttendanceDo.setStatus(type);
                updateStudentAttendanceList.add(studentAttendanceDo);
            }

        }
    }


}
